package com.mxpio.erp.mes.service.impl;

import com.mxpio.erp.common.enums.CommonEnums;
import com.mxpio.erp.common.enums.InspectionEnums;
import com.mxpio.erp.common.enums.MesEnums;
import com.mxpio.erp.common.enums.OrderStatusEnums;
import com.mxpio.erp.common.mes.entity.*;
import com.mxpio.erp.common.mes.service.MesScanInfoService;
import com.mxpio.erp.common.mes.vo.ScanInfoVo;
import com.mxpio.erp.common.quality.entity.*;
import com.mxpio.erp.common.technology.entity.*;
import com.mxpio.erp.common.util.MesScanUtil;
import com.mxpio.erp.common.workshop.entity.ManufactureOrder;
import com.mxpio.erp.common.workshop.entity.ManufactureOrderRoute;
import com.mxpio.erp.common.workshop.entity.ManufactureOrderRouteFixture;
import com.mxpio.erp.common.workshop.service.ManufactureOrderRouteService;
import com.mxpioframework.common.exception.MBootException;
import com.mxpioframework.common.vo.Result;
import com.mxpioframework.jpa.JpaUtil;
import com.mxpioframework.jpa.query.Criteria;
import com.mxpioframework.jpa.query.Order;
import com.mxpioframework.security.entity.User;
import com.mxpioframework.security.util.SecurityUtils;
import com.mxpioframework.system.service.impl.BaseServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
@Slf4j
public class MesScanInfoServiceImpl extends BaseServiceImpl<MesScanInfo> implements MesScanInfoService {

    private final String PROC_ROUT_END = "END";

    private final String PROC_MODE_JJ = "1";//机加
    private final String PROC_MODE_ZP = "2";//装配

    @Autowired
    private ManufactureOrderRouteService manufactureOrderRouteService;

    //加载作业单元 由当前登录人匹配对应的班组，取到班组对应的工作中心信息，向上取到车间信息，向下取到作业单元，整理好层级信息返回
    @Transactional
    @Override
    public Result<List<WorkShop>> loadWorkUnit(String procMode) {
        User loginUser = SecurityUtils.getLoginUser();//获取当前登录人
        List<WorkShop> returnWorkShopList = new ArrayList<>();
        if (loginUser != null) {
            List<WorkTeam> workTeams = JpaUtil.linq(WorkTeam.class)
                    .or()
                        .exists(WorkTeamMember.class)
                            .equalProperty("workTeamCode", "workTeamCode")
                            .equal("memberId", loginUser.getUsername())
                        .end()
                        .equal("teamLeader", loginUser.getUsername())
                    .end()
                    .list();
            if (workTeams.isEmpty()){
                return Result.error("当前登录人未指定班组，无法找到配置的车间和工作中心");
            }
            Map<String, List<WorkTeam>> wkshopMap = workTeams.stream().collect(Collectors.groupingBy(WorkTeam::getWkshopCode));

            for (String wkshopCode : wkshopMap.keySet()){
                List<WorkTeam> workTeamList = wkshopMap.get(wkshopCode);
                List<String> workCenterCodeList = workTeamList.stream().map(workTeam -> workTeam.getWorkCenterCode()).collect(Collectors.toList());
                WorkShop w = JpaUtil.getOne(WorkShop.class, wkshopCode);
                if (!StringUtils.equals(w.getProcMode(), procMode)){
                    continue;
                }
                List<WorkCenter> workCenterList = JpaUtil.linq(WorkCenter.class).in("workCenterCode", workCenterCodeList).list();
                if (!workCenterList.isEmpty()){
                    for (WorkCenter workCenter : workCenterList){
                        List<WorkUnit> workUnitList = JpaUtil.linq(WorkUnit.class)
                                .equal("mainWorkshop", workCenter.getWorkShopCode())
                                .equal("workCenterCode", workCenter.getWorkCenterCode())
                                .list();
                        workCenter.setWorkUnitList(workUnitList);
                    }
                    w.setWorkCenterList(workCenterList);
                }
                returnWorkShopList.add(w);
            }
        }else{
            return Result.error("未登录");
        }
        return Result.OK(returnWorkShopList);
    }


    @Transactional
    @Override
    public Result<List<ManufactureWorkOrderTask>> getWorkOrderTaskList(String workUnitCode) {
        if (StringUtils.isNotBlank(workUnitCode)){
            String statusPs = "20,30,90";//待作业、作业中、暂停中
            Criteria orderCri = Criteria.create();
            orderCri.addOrder(new Order("createTime",false));//默认按创建日期升序
            List<ManufactureWorkOrderTask> workOrderTaskList = JpaUtil.linq(ManufactureWorkOrderTask.class)
                    .equal("workUnitCode", workUnitCode)
                    .in("taskStatus", Stream.of(statusPs.split(",")).collect(Collectors.toList()))
                    .where(orderCri)
                    .list();
            return Result.OK(workOrderTaskList);
        }
        return Result.error("请选择作业单元！");
    }

    @Transactional
    @Override
    public Result<ScanInfoVo> loadWorkOrderTask(MesScanInfo operateInfo) {//加载工单工序任务
        String taskCode = operateInfo.getTaskCode();
        ManufactureWorkOrderTask orderTask = JpaUtil.getOne(ManufactureWorkOrderTask.class, taskCode);
        if (orderTask==null){
            return Result.error("工单任务编码无效，请检查");
        }

        String bizNo = orderTask.getBizNo();
        String batchCode = orderTask.getBatchCode();
        ManufactureWorkOrder workOrder = JpaUtil.getOne(ManufactureWorkOrder.class, batchCode);
        String workUnitCode = operateInfo.getWorkUnitCode();
        WorkUnit workUnit = JpaUtil.getOne(WorkUnit.class, workUnitCode);
        if (workUnit==null){
            return Result.error("作业单元无效");
        }
        if (!StringUtils.equals(orderTask.getProcCode(),workUnit.getProcessCode())){
            return Result.error("工单工序任务与作业单元工序不匹配");
        }
        if (orderTask.getProcOrder()!=workOrder.getNextProcOrder()){
            return Result.error("工单任务未执行到当前工序任务");
        }
        ManufactureOrder manufactureOrder = JpaUtil.getOne(ManufactureOrder.class, bizNo);

        String productItemCode = manufactureOrder.getProductItemCode();
        Item productItem = JpaUtil.linq(Item.class).equal("itemCode", productItemCode).findOne();
        if (productItem==null){
            return Result.error("订单产品编码无效！");
        }
        ItemQualityProp productQualityProp = JpaUtil.linq(ItemQualityProp.class).equal("propType", "item").equal("code", productItemCode).findOne();
        if (productQualityProp==null){
            return Result.error("未维护订单产品的质量属性数据！");
        }
        //统计生产订单已完工数量
        int orderCompNum = getMOCompNum(workOrder.getBizNo());
        //查询产品设计图纸
        List<ItemDesignFile> itemDesignFiles = JpaUtil.linq(ItemDesignFile.class).equal("itemCode", manufactureOrder.getProductItemCode()).equal("primary", "1").list();
        //查询工艺图纸
        List<ProdTecRoutProcTechDrawing> procTechDrawings = JpaUtil.linq(ProdTecRoutProcTechDrawing.class).equal("routProcId", orderTask.getRoutProcId()).equal("primary", "1").list();
        //查询订单工艺路线工序上的工装夹具
        List<ManufactureOrderRouteFixture> procFixtures = JpaUtil.linq(ManufactureOrderRouteFixture.class).equal("orderRoutProcId", orderTask.getOrderRoutProcId()).list();


        ManufactureOrderRoute workUnitProcRoute = JpaUtil.linq(ManufactureOrderRoute.class).collect("processCode", ProcessInfo.class,"processCode").equal("bizNo", workOrder.getBizNo()).equal("routProcId",orderTask.getRoutProcId()).findOne();
        if (workUnitProcRoute==null){
            return Result.error("未匹配到订单工艺路线对应工序信息");
        }


        ScanInfoVo returnVo = new ScanInfoVo();
        returnVo.setManufactureOrder(manufactureOrder);
        returnVo.setManufactureWorkOrder(workOrder);
        returnVo.setManufactureWorkOrderTask(orderTask);
        returnVo.setItemDesignFiles(itemDesignFiles);
        returnVo.setProcTechDrawings(procTechDrawings);
        returnVo.setProcFixtures(procFixtures);
        returnVo.setOrderCompNum(orderCompNum);
        returnVo.setProductItem(productItem);
        returnVo.setProductQualityProp(productQualityProp);
        returnVo.setManufactureOrderRoute(workUnitProcRoute);
        return Result.OK(returnVo);
    }

    @Transactional(readOnly = true)
    @Override
    public Result<ScanInfoVo> scanWorkOrderTaskCode(ScanInfoVo scanInfoVO) {
        MesScanInfo operateInfo = new MesScanInfo(scanInfoVO);
        operateInfo.setOperationType("工单工序任务扫描");
        String taskCode = scanInfoVO.getTaskCode();
        ManufactureWorkOrderTask orderTask = JpaUtil.getOne(ManufactureWorkOrderTask.class, taskCode);
        if (orderTask==null){
            return Result.error("工单工序任务不存在");
        }
        if(!StringUtils.equals(orderTask.getTaskStatus(),  MesEnums.WorkOrderTaskStatus.WORKING.getCode())&&
                !StringUtils.equals(orderTask.getTaskStatus(),  MesEnums.WorkOrderTaskStatus.READY.getCode())){//todo 工序任务状态判断
            return Result.error("工单工序任务状态不允许操作");
        }
        String batchCode = orderTask.getBatchCode();//工单单号
        ManufactureWorkOrder workOrder = JpaUtil.getOne(ManufactureWorkOrder.class, batchCode);
        if (!StringUtils.equals(workOrder.getBatchStatus(), MesEnums.WorkOrderStatus.WORKING.getCode())
                &&!StringUtils.equals(workOrder.getBatchStatus(), MesEnums.WorkOrderStatus.SEND.getCode())){
            return Result.error("工单状态有误");
        }
        Integer nextProcOrder = workOrder.getNextProcOrder();
        if (orderTask.getProcOrder()!=nextProcOrder){
            return Result.error("工单任务未执行到当前工序任务");
        }
        String workUnitCode = scanInfoVO.getWorkUnitCode();
        WorkUnit workUnit = JpaUtil.getOne(WorkUnit.class, workUnitCode);
        if (workUnit==null){
            return Result.error("未找到作业单元");
        }
        if (!StringUtils.equals(workUnit.getStatus(), CommonEnums.YesNo.YES.getCode())){
            return Result.error("作业单元未启用");
        }
        if (!StringUtils.equals(workUnit.getWorkCenterCode(),workOrder.getWorkCenterCode())){
            return Result.error("作业单元工作中心和工单工作中心不一致");
        }
        if (!StringUtils.equals(workUnit.getProcessCode(),orderTask.getProcCode())){
            return Result.error("作业单元工序和工单工序任务不一致");
        }
        Criteria orderRouteCri = Criteria.create();
        orderRouteCri.addOrder(new Order("processOrder",false));

        List<ManufactureOrderRoute> orderRouteList = JpaUtil.linq(ManufactureOrderRoute.class).equal("bizNo", orderTask.getBizNo()).collect("processCode", ProcessInfo.class,"processCode").where(orderRouteCri).list();
        if (orderRouteList.isEmpty()){
            return Result.error("未找到订单工艺路线");
        }
        Integer nextProcOrderNew = null;
        String nextProcCode = null;
        ManufactureOrderRoute nextRout = null;
        for (ManufactureOrderRoute orderRoute : orderRouteList){
            if (orderRoute.getProcessOrder()>nextProcOrder){
                nextProcOrderNew = orderRoute.getProcessOrder();
                nextProcCode = orderRoute.getProcessCode();
                nextRout = orderRoute;
                break;
            }
        }
        if (nextProcOrderNew==null){
            nextProcCode = PROC_ROUT_END;
        }
        //
        orderTask.setTaskStatus(MesEnums.WorkOrderTaskStatus.FINISH.getCode());//更新工序任务状态
        orderTask.setReEndDate(new Date());
        orderTask.setReEndUserId(SecurityUtils.getLoginUsername());
        JpaUtil.update(orderTask);


        if (!StringUtils.equals(nextProcCode,PROC_ROUT_END)){
            workOrder.setNextProcOrder(nextProcOrderNew);
            if (!StringUtils.equals(workOrder.getBatchStatus(), MesEnums.WorkOrderStatus.WORKING.getCode())){
                workOrder.setBatchStatus(MesEnums.WorkOrderStatus.WORKING.getCode());//加工中
            }
        }else{
            workOrder.setBatchStatus(MesEnums.WorkOrderStatus.FINISH.getCode());
        }
        JpaUtil.update(workOrder);
        JpaUtil.save(operateInfo);//保存扫描日志
        return Result.OK();
    }

    @Override
    @Transactional
    public Result<ScanInfoVo> scanAssemble(ScanInfoVo scanInfoVO) {//装配扫码(批次条码)报工
        try{
            scanPreCheck(scanInfoVO, PROC_MODE_ZP);
            handleScan(scanInfoVO);
        }catch (MBootException mBootException){
            return Result.error(mBootException.getMessage());
        }
        return Result.OK("成功报工，数量："+scanInfoVO.getReportNum()+"！",scanInfoVO);
    }

    @Override
    @Transactional
    public Result<ScanInfoVo> scanMachine(ScanInfoVo scanInfoVO) {
        try{
            scanPreCheck(scanInfoVO,PROC_MODE_JJ);
            handleScan(scanInfoVO);
        }catch (MBootException mBootException){
            return Result.error(mBootException.getMessage());
        }
        return Result.OK("成功报工，数量："+scanInfoVO.getReportNum()+"！",scanInfoVO);
    }
    private void handleScan(ScanInfoVo scanInfoVO) throws MBootException{
        String batchWorkEnable = scanInfoVO.getBatchWorkEnable();//分批报工
        Date now = new Date();
        scanInfoVO.setScanTime(now);
        ManufactureOrder manufactureOrder = scanInfoVO.getManufactureOrder();
        List<ManufactureOrderRoute> orderRouteList = scanInfoVO.getOrderRouteList();
        ManufactureWorkOrder workOrder = scanInfoVO.getManufactureWorkOrder();
        ManufactureWorkOrderTask orderTask = scanInfoVO.getManufactureWorkOrderTask();
        WorkUnit workUnit = scanInfoVO.getWorkUnit();
        Integer nextProcOrder = workOrder.getNextProcOrder();
        boolean workerOrderUpdateFlag = false;

        ManufactureOrderRoute currentRoute = orderRouteList.stream().filter(o -> o.getProcessOrder() == nextProcOrder).findFirst().get();
        if (currentRoute==null){
            throw new MBootException("根据工单工序任务指针，未能匹配到工艺路线对应工序，无法继续！");
        }
        boolean isComWorkEnabe = true;
        ProcessInfo currentRouteProcessInfo = currentRoute.getProcessInfo();
        String finishCheckFlag = currentRouteProcessInfo.getFinishCheckFlag();//是否完工检
        String toleranceRange = currentRouteProcessInfo.getToleranceRange();//允差范围
        if (StringUtils.isBlank(toleranceRange)){
            toleranceRange = "100";
        }
        if (StringUtils.equals(CommonEnums.YesNo.YES.getCode(),batchWorkEnable)){//允许分批报工
            int wotCompNum = orderTask.getCompNum()==null?0:orderTask.getCompNum();//getWOTCompNum(orderTask.getTaskCode());//已报工数量
            Integer taskNum = orderTask.getTaskNum();//批次数量
            Integer scrapNum = orderTask.getScrapNum()==null?0:orderTask.getScrapNum();//报废数量
            Integer splitBatchNum = scanInfoVO.getReportNum();//本次分批报工数量
            if ((taskNum*(Float.parseFloat(toleranceRange))/100+taskNum)<(wotCompNum+scrapNum+splitBatchNum)){
                throw new MBootException("报工数量有误，超过允差范围上限");
            }

            Boolean isScanEnable = taskNum-taskNum*(Float.parseFloat(toleranceRange))/100>(scrapNum+wotCompNum+splitBatchNum);//N表示不满足报工条件
            if (isScanEnable){
                isComWorkEnabe = false;//不满足完工条件
            }
            orderTask.setCompNum(wotCompNum+splitBatchNum);
            if (!StringUtils.equals(orderTask.getTaskStatus(), MesEnums.WorkOrderTaskStatus.WORKING.getCode())){
                orderTask.setTaskStatus(MesEnums.WorkOrderTaskStatus.WORKING.getCode());//加工中
            }
            //持久化
            ManufactureWorkOrderTaskSplit taskSplit = new ManufactureWorkOrderTaskSplit(orderTask);
            taskSplit.setSplitTaskNum(splitBatchNum);
            JpaUtil.save(taskSplit);
        }else{
            Integer batchNum = scanInfoVO.getReportNum();//本次报工数量
            Integer taskNum = orderTask.getTaskNum();//批次数量
            Integer scrapNum = orderTask.getScrapNum()==null?0:orderTask.getScrapNum();//报废数量

            if ((taskNum*(Float.parseFloat(toleranceRange))/100+taskNum)<(scrapNum+batchNum)){
                throw new MBootException("报工数量有误，超过允差范围上限");
            }

            Boolean isScanEnable = taskNum*(Float.parseFloat(toleranceRange))/100>=(taskNum-scrapNum-batchNum);
            if (!isScanEnable){
                throw new MBootException("报工数量有误，不能低于允差范围下限");
            }
            orderTask.setCompNum(batchNum);
        }
        //处理sn关联关系
        List<ManufactureWorkOrderSn> snList = scanInfoVO.getSnList();
        if (snList!=null&& !snList.isEmpty()){
            handleTaskSnRelation(snList,orderTask);
        }

        if (isComWorkEnabe){
            Integer nextProcOrderNew = null;
            String nextProcCode = null;
            ManufactureOrderRoute nextRout = MesScanUtil.getOrderNextProcRoute(orderRouteList, nextProcOrder);
            if (nextRout==null){
                nextProcCode = PROC_ROUT_END;
            }else{
                nextProcOrderNew = nextRout.getProcessOrder();
                nextProcCode = nextRout.getProcessCode();
            }
            String firstInspectNeeded = workOrder.getFirstInspectNeeded();//当前工单是否是首检批

            //记录实际报工时间
            orderTask.setReEndDate(now);
            orderTask.setReEndUserId(SecurityUtils.getLoginUsername());
            //非最后一道工序
            if (!StringUtils.equals(nextProcCode,PROC_ROUT_END)){
                //判断是否需要产生工序质检单
                if (StringUtils.equals(CommonEnums.YesNo.YES.getCode(),finishCheckFlag)){//工序开启完工检
                    String schemeCode = currentRoute.getSchemeCode();
                    generateOrderTaskInpectionBill(orderTask,schemeCode);
                    orderTask.setInspectionBillChecking(CommonEnums.YesNo.YES.getCode());
                    orderTask.setTaskStatus(MesEnums.WorkOrderTaskStatus.WORKING.getCode());//加工中
                }

                if (!StringUtils.equals(orderTask.getInspectionBillChecking(),CommonEnums.YesNo.YES.getCode())){//非检验中状态
                    workOrder.setNextProcOrder(nextProcOrderNew);//更新工单工序任务指针
                    orderTask.setTaskStatus(MesEnums.WorkOrderTaskStatus.FINISH.getCode());//更新工序任务状态
                    workerOrderUpdateFlag = true;
                }
                if (!StringUtils.equals(workOrder.getBatchStatus(), MesEnums.WorkOrderStatus.WORKING.getCode())){
                    workOrder.setBatchStatus(MesEnums.WorkOrderStatus.WORKING.getCode());//加工中
                    workerOrderUpdateFlag = true;
                }
            }else{//最后一道工序
                if (StringUtils.equals(firstInspectNeeded, CommonEnums.YesNo.YES.getCode())){//当前批次为首检批
                    InspectionSchemeChain productSchemeChain = JpaUtil.linq(InspectionSchemeChain.class)
                            .equal("busiType", InspectionEnums.ISBusiType.Process.getCode())
                            .equal("itemCode", manufactureOrder.getProductItemCode()).findOne();
                    if (productSchemeChain!=null){
                        workOrder.setBatchCompNum(orderTask.getCompNum());//取任务完工数量
                        generateWorkOrderInpectionBill(manufactureOrder.getProductItemCode(), workOrder,productSchemeChain.getSchemeCode());
                        workOrder.setBatchStatus(MesEnums.WorkOrderStatus.WORKING.getCode());//加工中
                        workOrder.setInspectionBillChecking(CommonEnums.YesNo.YES.getCode());
                        workerOrderUpdateFlag = true;
                    }else{
                        log.warn("首检工单产生质检单时未匹配到对应的物料制程检测方案！");
                        workOrder.setBatchCompNum(orderTask.getCompNum());//取任务完工数量
                        workOrder.setBatchScrapNum(orderTask.getScrapNum());//取任务报废数量
                        workOrder.setBatchStatus(MesEnums.WorkOrderStatus.FINISH.getCode());
                        workerOrderUpdateFlag = true;
                    }
                    orderTask.setTaskStatus(MesEnums.WorkOrderTaskStatus.FINISH.getCode());//更新工序任务状态
                }else{
                    //判断是否需要产生工序质检单
                    if (StringUtils.equals(CommonEnums.YesNo.YES.getCode(),finishCheckFlag)){//工序开启完工检
                        String schemeCode = currentRoute.getSchemeCode();
                        generateOrderTaskInpectionBill(orderTask,schemeCode);
                        orderTask.setInspectionBillChecking(CommonEnums.YesNo.YES.getCode());
                        orderTask.setTaskStatus(MesEnums.WorkOrderTaskStatus.WORKING.getCode());//加工中
                    }
                    if (!StringUtils.equals(orderTask.getInspectionBillChecking(),CommonEnums.YesNo.YES.getCode())){//非检验中状态
                        orderTask.setTaskStatus(MesEnums.WorkOrderTaskStatus.FINISH.getCode());//更新工序任务状态

                        workOrder.setBatchCompNum(orderTask.getCompNum());//取任务完工数量
                        workOrder.setBatchScrapNum(orderTask.getScrapNum());//取任务报废数量
                        workOrder.setBatchStatus(MesEnums.WorkOrderStatus.FINISH.getCode());
                        workerOrderUpdateFlag = true;
                    }
                }
            }
            if (StringUtils.equals(orderTask.getTaskStatus(), MesEnums.WorkOrderTaskStatus.FINISH.getCode())){//任务报工完成时记录info日志
                MesScanInfo operateInfo = new MesScanInfo(scanInfoVO);
                operateInfo.setProcCode(orderTask.getProcCode());
                operateInfo.setProcOrder(orderTask.getProcOrder());
                operateInfo.setTaskCode(orderTask.getTaskCode());
                operateInfo.setProcGroupCode(orderTask.getProcGroupCode());
                operateInfo.setProcGroupName(orderTask.getProcGroupName());
                operateInfo.setOperationType("工单工序任务扫描");
                operateInfo.setScanTime(now);
                operateInfo.setCompletedNum(orderTask.getCompNum());
                operateInfo.setScrapNum(orderTask.getScrapNum());
                operateInfo.setOperator(SecurityUtils.getLoginUsername());
                JpaUtil.save(operateInfo);//保存扫描日志
            }
        }else{
            if (!StringUtils.equals(workOrder.getBatchStatus(), MesEnums.WorkOrderStatus.WORKING.getCode())){
                workOrder.setBatchStatus(MesEnums.WorkOrderStatus.WORKING.getCode());//加工中
                workerOrderUpdateFlag = true;
            }
        }
        JpaUtil.update(orderTask);
        if (workerOrderUpdateFlag){
            JpaUtil.update(workOrder);
        }
    }

    private void scanPreCheck(ScanInfoVo scanInfoVO,String procMode) throws MBootException {//扫码前校验
        String bizNo = scanInfoVO.getBizNo();
        if(StringUtils.isBlank(bizNo)){
            throw new MBootException("订单编号为空！");
        }
        ManufactureOrder manufactureOrder = JpaUtil.linq(ManufactureOrder.class).equal("bizNo", bizNo).findOne();
        if(manufactureOrder==null){
            throw new MBootException("生产订单不存在！");
        }
        if (manufactureOrder.getCloseStatus().equals(OrderStatusEnums.CloseStatus.CLOSED.getStatusCode())){
            throw new MBootException("生产订单已关闭");
        }
        if (!(manufactureOrder.getOrderStatus().equals(OrderStatusEnums.OrderStatus.ORDERED.getStatusCode())
                ||manufactureOrder.getOrderStatus().equals(OrderStatusEnums.OrderStatus.PROCESSING.getStatusCode()))){
            throw new MBootException("生产订单状态有误，仅允许已下达、执行中订单状态！");
        }
        scanInfoVO.setManufactureOrder(manufactureOrder);

        String batchCode = scanInfoVO.getBatchCode();//工单单号
        if(StringUtils.isEmpty(batchCode)){
            throw new MBootException("批次条码为空！");
        }
        ManufactureWorkOrder workOrder = JpaUtil.getOne(ManufactureWorkOrder.class, batchCode);
        if (workOrder==null){
            throw new MBootException("批次条码不存在");
        }
        if (!StringUtils.equals(workOrder.getBatchStatus(), MesEnums.WorkOrderStatus.WORKING.getCode())
                &&!StringUtils.equals(workOrder.getBatchStatus(), MesEnums.WorkOrderStatus.SEND.getCode())){
            throw new MBootException("工单批次状态有误");
        }
        if (StringUtils.equals(workOrder.getInspectionBillChecking(),CommonEnums.YesNo.YES.getCode())){
            throw new MBootException("首检批对应产品检验质检单已生成，请先处理质检单！");
        }
        scanInfoVO.setManufactureWorkOrder(workOrder);

        String workUnitCode = scanInfoVO.getWorkUnitCode();
        if(StringUtils.isBlank(workUnitCode)){
            throw new MBootException("作业单元编号为空！");
        }
        WorkUnit workUnit = JpaUtil.getOne(WorkUnit.class, workUnitCode);
        if (workUnit==null){
            throw new MBootException("未找到作业单元");
        }
        if (!StringUtils.equals(workUnit.getStatus(), CommonEnums.YesNo.YES.getCode())){
            throw new MBootException("作业单元未启用");
        }
        scanInfoVO.setWorkUnit(workUnit);

        ManufactureWorkOrderTask orderTask = null;
        if (StringUtils.equals(PROC_MODE_JJ, procMode)){//机加
            String taskCode = scanInfoVO.getTaskCode();
            if(StringUtils.isBlank(taskCode)){
                throw new MBootException("工单工序任务编号为空！");
            }
            orderTask = JpaUtil.getOne(ManufactureWorkOrderTask.class, taskCode);
            if (orderTask==null){
                throw new MBootException("工单工序任务编号无效！");
            }
        }else if (StringUtils.equals(PROC_MODE_ZP, procMode)){//装配
            String statusPs = "20,30,90";//待作业、作业中、暂停中
            Criteria orderCri = Criteria.create();
            orderCri.addOrder(new Order("procOrder",false));//工序序号
            List<ManufactureWorkOrderTask> workOrderTaskList = JpaUtil.linq(ManufactureWorkOrderTask.class)
                    .equal("workUnitCode", workUnitCode)
                    .equal("batchCode", batchCode)
                    .in("taskStatus", Stream.of(statusPs.split(",")).collect(Collectors.toList()))
                    .where(orderCri)
                    .list();
            if (workOrderTaskList.isEmpty()){
                throw new MBootException("未找匹配到工单工序任务");
            }
            if (workOrderTaskList.size()>1){
                log.warn("存在工序重入情况，默认取第一个未处理的任务！");
            }
            orderTask = workOrderTaskList.get(0);
        }else{
            throw new MBootException("加工模式有误");
        }

        if(!StringUtils.equals(orderTask.getTaskStatus(),  MesEnums.WorkOrderTaskStatus.WORKING.getCode())&&
                !StringUtils.equals(orderTask.getTaskStatus(),  MesEnums.WorkOrderTaskStatus.READY.getCode())){//todo 工序任务状态判断
            throw new MBootException("工单工序任务状态不允许操作");
        }

        Integer nextProcOrder = workOrder.getNextProcOrder();
        if (orderTask.getProcOrder()!=nextProcOrder){
            throw new MBootException("工单任务未执行到当前工序任务");
        }
        if (!StringUtils.equals(workUnit.getWorkCenterCode(),workOrder.getWorkCenterCode())){
            throw new MBootException("作业单元工作中心和工单工作中心不一致");
        }
        if (!StringUtils.equals(workUnit.getProcessCode(),orderTask.getProcCode())){
            throw new MBootException("作业单元工序和工单工序任务不一致");
        }

        if (StringUtils.equals(orderTask.getInspectionBillChecking(),CommonEnums.YesNo.YES.getCode())){
            throw new MBootException("已产生工序制程质检单，请先处理质检单！");
        }
        scanInfoVO.setManufactureWorkOrderTask(orderTask);

        //订单工艺路线
        List<ManufactureOrderRoute> orderRouteList = manufactureOrderRouteService.list(manufactureOrder.getBizNo());
        if (orderRouteList.isEmpty()){
            throw new MBootException("未找到订单工艺路线");
        }
        scanInfoVO.setOrderRouteList(orderRouteList);

        //判断是否存在首检批，以及首检批是否已经完成
        if (!StringUtils.equals(workOrder.getFirstInspectNeeded(), CommonEnums.YesNo.YES.getCode())){//非首检批的情况
            ManufactureWorkOrder firstInspectionBatch = JpaUtil.linq(ManufactureWorkOrder.class)
                    .equal("firstInspectNeeded", CommonEnums.YesNo.YES.getCode())
                    .equal("bizNo", manufactureOrder.getBizNo())
                    .findOne();
            if (firstInspectionBatch!=null){
                if (StringUtils.equals(firstInspectionBatch.getInspectionBillChecking(),CommonEnums.YesNo.YES.getCode())){
                    throw new MBootException("首检批对应产品检验质检单未检测完成，常规批次不能报工！");
                }

                if (!StringUtils.equals(firstInspectionBatch.getBatchStatus(), MesEnums.WorkOrderStatus.FINISH.getCode())){
                    throw new MBootException("首检批未完成，常规批次不能报工");
                }

            }
        }/*else{
            InspectionBill generatedWorkOrderInspectionBill = JpaUtil.linq(InspectionBill.class)
                    .equal("busiType", InspectionEnums.ISBusiType.Process.getCode())
                    .equal("originWorkOrderCode",workOrder.getBatchCode())
                    .findOne();
            if (generatedWorkOrderInspectionBill!=null){
                throw new MBootException("首检批对应产品检验质检单已生成，请先处理质检单！");
            }
        }*/

        /*//查询工序任务是否存在已生成的工序制程质检单
        InspectionBill generatedWorkOrderTaskInspectionBill = JpaUtil.linq(InspectionBill.class)
                .equal("busiType", InspectionEnums.ISBusiType.Process.getCode())
                .equal("originWorkOrderTaskCode",orderTask.getBatchCode())
                .findOne();
        if (generatedWorkOrderTaskInspectionBill!=null){
            //scanInfoVO.setWorkOrderTaskInspectionBill(generatedWorkOrderTaskInspectionBill);
            throw new MBootException("已产生工序制程质检单，请先处理质检单！");
        }*/
    }

    @Override
    @Transactional
    //@Deprecated //装配加载批次信息
    public Result<ScanInfoVo> loadWorkOrderTaskByBatchCode(MesScanInfo operateInfo) {//同步批次号加载对应任务
        String batchCode = operateInfo.getBatchCode();
        String workUnitCode = operateInfo.getWorkUnitCode();
        ManufactureWorkOrder workOrder = JpaUtil.getOne(ManufactureWorkOrder.class, batchCode);
        if (workOrder==null){
            return Result.error("条码（批次号）无效");
        }
        String statusPs = "20,30,90";//待作业、作业中、暂停中
        Criteria orderCri = Criteria.create();
        orderCri.addOrder(new Order("procOrder",false));//工序序号
        List<ManufactureWorkOrderTask> workOrderTaskList = JpaUtil.linq(ManufactureWorkOrderTask.class)
                .equal("workUnitCode", workUnitCode)
                .equal("batchCode", batchCode)
                .in("taskStatus", Stream.of(statusPs.split(",")).collect(Collectors.toList()))
                .where(orderCri)
                .list();
        if (workOrderTaskList.isEmpty()){
            return Result.error("未找匹配到工单工序任务");
        }
        if (workOrderTaskList.size()>1){
            //return Result.error("数据有误！匹配到多条该作业单元对应的工序任务！！");
            log.warn("存在工序重入情况，默认取第一个未处理的任务！");
        }
        ManufactureWorkOrderTask orderTask = workOrderTaskList.get(0);
        String bizNo = workOrder.getBizNo();
        WorkUnit workUnit = JpaUtil.getOne(WorkUnit.class, workUnitCode);
        if (!StringUtils.equals(orderTask.getProcCode(),workUnit.getProcessCode())){
            return Result.error("工单工序任务与作业单元工序不匹配");
        }
        if (orderTask.getProcOrder()!=workOrder.getNextProcOrder()){
            return Result.error("工单任务未执行到当前工序任务");
        }
        ScanInfoVo returnVo = new ScanInfoVo();
        returnVo.setManufactureWorkOrder(workOrder);
        returnVo.setManufactureWorkOrderTask(orderTask);
        //分批报工时，统计已报工数量，已做持久化字段，不需要实时汇总
        //orderTask.setCompNum(getWOTCompNum(orderTask.getTaskCode()));
        return Result.OK(returnVo);
    }

    @Override
    @Transactional
    public Result<ScanInfoVo> loadManufactureOrderByBatchCode(ScanInfoVo operateInfo) {
        String batchCode = operateInfo.getBatchCode();
        ManufactureWorkOrder workOrder = JpaUtil.getOne(ManufactureWorkOrder.class, batchCode);
        if (workOrder==null){
            return Result.error("条码（批次号）无效");
        }
        if(!StringUtils.equals(workOrder.getWorkCenterCode(),operateInfo.getWorkCenterCode())){
            return Result.error("登录机台工作中心和批次工单工作中心不一致！");
        }
        String workUnitCode = operateInfo.getWorkUnitCode();
        WorkUnit workUnit = JpaUtil.getOne(WorkUnit.class, workUnitCode);
        if (workUnit==null){
            return Result.error("作业单元无效");
        }
        ManufactureOrder manufactureOrder = JpaUtil.getOne(ManufactureOrder.class, workOrder.getBizNo());
        String productItemCode = manufactureOrder.getProductItemCode();
        Item productItem = JpaUtil.linq(Item.class).equal("itemCode", productItemCode).findOne();
        if (productItem==null){
            return Result.error("订单产品编码无效！");
        }
        ItemQualityProp productQualityProp = JpaUtil.linq(ItemQualityProp.class).equal("propType", "item").equal("code", productItemCode).findOne();
        if (productQualityProp==null){
            return Result.error("未维护订单产品的质量属性数据！");
        }
        //todo 统计已报完工数量逻辑
        int orderCompNum = getMOCompNum(workOrder.getBizNo());
        //查询产品设计图纸
        List<ItemDesignFile> itemDesignFiles = JpaUtil.linq(ItemDesignFile.class).equal("itemCode", manufactureOrder.getProductItemCode()).equal("primary", "1").list();
        //查询订单工艺路路线
        Criteria orderRouteCri = Criteria.create();
        orderRouteCri.addOrder(new Order("processOrder",false));
        List<ManufactureOrderRoute> orderRouteList = JpaUtil.linq(ManufactureOrderRoute.class).collect("processCode", ProcessInfo.class,"processCode").equal("bizNo", workOrder.getBizNo()).where(orderRouteCri).list();
        if (orderRouteList.isEmpty()){
            return Result.error("订单工艺路线为空");
        }
        ManufactureOrderRoute workUnitProcRoute = null;
        for (ManufactureOrderRoute route:orderRouteList){//todo:工序重入的情况要如何处理？
            if (StringUtils.equals(route.getProcessCode(), workUnit.getProcessCode())){
                workUnitProcRoute = route;
                break;
            }
        }
        List<ProdTecRoutProcTechDrawing> procTechDrawings = new ArrayList<>();
        if (workUnitProcRoute!=null){
            //查询工艺图纸
            procTechDrawings = JpaUtil.linq(ProdTecRoutProcTechDrawing.class).equal("routProcId", workUnitProcRoute.getRoutProcId()).equal("primary", "1").list();
        }else{
            return Result.error("订单和作业单元不匹配！");
        }

        ScanInfoVo returnVo = new ScanInfoVo();
        returnVo.setManufactureOrder(manufactureOrder);
        //returnVo.setManufactureWorkOrder(workOrder);
        returnVo.setItemDesignFiles(itemDesignFiles);
        returnVo.setProcTechDrawings(procTechDrawings);
        returnVo.setOrderCompNum(orderCompNum);
        returnVo.setProductItem(productItem);
        returnVo.setProductQualityProp(productQualityProp);
        returnVo.setManufactureOrderRoute(workUnitProcRoute);
        return Result.OK(returnVo);
    }

    private int getMOCompNum(String bizNo){//统计生产订单已完成工单数量合计
        int orderCompNum = 0;
        List<ManufactureWorkOrder> orderItemList = JpaUtil.linq(ManufactureWorkOrder.class).equal("batchStatus", MesEnums.WorkOrderStatus.FINISH.getCode()).equal("bizNo", bizNo).list();
        if (!orderItemList.isEmpty()){
            orderCompNum = orderItemList.stream().mapToInt(ManufactureWorkOrder::getBatchNum).reduce(0, Integer::sum);
        }
        return orderCompNum;
    }
    private int getWOTCompNum(String taskCode){//统计工单工序任务分批报工已报工数量合计
        int wotCompNum = 0;
        List<ManufactureWorkOrderTaskSplit> splitList = JpaUtil.linq(ManufactureWorkOrderTaskSplit.class).equal("taskCode", taskCode).list();
        if (!splitList.isEmpty()){
            wotCompNum = splitList.stream().map(ManufactureWorkOrderTaskSplit::getSplitTaskNum).reduce(0, Integer::sum);
        }
        return wotCompNum;
    }

    private void handleTaskSnRelation(List<ManufactureWorkOrderSn> snList, ManufactureWorkOrderTask task){
        Set<String> sncodes = snList.stream().map(ManufactureWorkOrderSn::getSnCode).collect(Collectors.toSet());
        List<ManufactureWorkOrderSn> dbSnList = JpaUtil.linq(ManufactureWorkOrderSn.class).in("snCode", sncodes).list();
        Map<String, ManufactureWorkOrderSn> dbSnMap = JpaUtil.index(dbSnList);
        for (ManufactureWorkOrderSn sn:snList){
            ManufactureWorkOrderSn db = dbSnMap.get(sn.getSnCode());
            if (StringUtils.isBlank(db.getBatchCode())){
                db.setBatchCode(task.getBatchCode());
                db.setBizNo(task.getBizNo());
                JpaUtil.update(db);
            }
            ManufactureWorkOrderTaskSn taskSn = new ManufactureWorkOrderTaskSn();
            taskSn.setSnCode(sn.getSnCode());
            taskSn.setBizNo(task.getBizNo());
            taskSn.setBatchCode(task.getBatchCode());
            taskSn.setTaskCode(task.getTaskCode());
            JpaUtil.save(taskSn);
        }
    }

    private void generateOrderTaskInpectionBill(ManufactureWorkOrderTask orderTask,String schemeCode){//根据产品工艺路线工序信息上维护的质检方案，生成对应的质检单
        InspectionBill bill = new InspectionBill(orderTask);
        bill.setSchemeCode(schemeCode);
        JpaUtil.save(bill);
        //generateInpectionBillDetails(bill,schemeCode);不产生检测项和样本
    }
    private void generateWorkOrderInpectionBill(String itemCode,ManufactureWorkOrder workOrder,String schemeCode){//根据产品工艺路线工序信息上维护的质检方案，生成对应的质检单
        InspectionBill bill = new InspectionBill(workOrder);
        Item dbItem = JpaUtil.getOne(Item.class, itemCode);//物料信息
        bill.setItemCode(dbItem.getItemCode());
        bill.setItemName(dbItem.getItemName());
        bill.setUnitCode(dbItem.getUnitCode());
        bill.setItemSpec(dbItem.getItemSpec());
        bill.setSchemeCode(schemeCode);
        JpaUtil.save(bill);
        //generateInpectionBillDetails(bill,schemeCode);
    }

    private void generateInpectionBillDetails(InspectionBill bill,String schemeCode){//生成质检单样本、质检明细
        String itemCode = bill.getItemCode();//查询物料信息
        Item dbItem = JpaUtil.getOne(Item.class, itemCode);//物料信息
        InspectionScheme inspectionScheme = JpaUtil.getOne(InspectionScheme.class, schemeCode);
        List<InspectionSchemeLine> inspectionSchemeLines = JpaUtil.linq(InspectionSchemeLine.class)
                .collect("code", InspectionItem.class, "code")
                .equal("schemeCode", schemeCode)
                .list();

        int detectNum = 0;
        List<InspectionBillDetect> detectList = new ArrayList<>();
        for (InspectionSchemeLine line:inspectionSchemeLines){
            detectNum++;
            InspectionBillDetect inspectionBillDetect = new InspectionBillDetect();
            inspectionBillDetect.setBizNo(bill.getBizNo());
            inspectionBillDetect.setDetectCode(String.format("%04d",detectNum));
            inspectionBillDetect.setSort(detectNum);
            inspectionBillDetect.setDetectSource("1");//1物料、2物料组
            inspectionBillDetect.setSchemeCode(inspectionScheme.getCode());
            inspectionBillDetect.setSimpleQuantity(bill.getCheckQuantity());
            //保存 关联检测项目
            InspectionItem inspectionItem = line.getInspectionItem();
            inspectionBillDetect.setInspectionItemCode(inspectionItem.getCode());
            inspectionBillDetect.setInspectionItemName(inspectionItem.getName());
            inspectionBillDetect.setInspectionItemClass(inspectionItem.getItemClass());
            inspectionBillDetect.setInspectionItemType(inspectionItem.getItemType());
            inspectionBillDetect.setInspectionItemDefectGrade(inspectionItem.getDefectGrade());
            inspectionBillDetect.setInspectionItemStandard(inspectionItem.getStandard());
            inspectionBillDetect.setInspectionItemDetectionTool(inspectionItem.getDetectionTool());
            inspectionBillDetect.setInspectionItemDetectionMethod(inspectionItem.getDetectionMethod());
            inspectionBillDetect.setInspectionItemTechnicalRequirements(inspectionItem.getTechnicalRequirements());
            //保存 关联检测方案行
            inspectionBillDetect.setComparator(line.getComparator());
            inspectionBillDetect.setTargetValue(line.getTargetValue());
            inspectionBillDetect.setUnits(line.getUnits());
            inspectionBillDetect.setMaxValue(line.getMaxValue());
            inspectionBillDetect.setMinValue(line.getMinValue());

            //查询 检测项目 参数选项对应默认值
            List<InspectionItemParam> inspectionItemParams = JpaUtil.linq(InspectionItemParam.class).equal("code", inspectionItem.getCode()).equal("defaultParam", Boolean.TRUE).list();
            if(!inspectionItemParams.isEmpty()){
                inspectionBillDetect.setDefaultMeasurement(inspectionItemParams.get(0).getParameter());
            }

            JpaUtil.save(inspectionBillDetect);
            detectList.add(inspectionBillDetect);
        }

        List<InspectionBillSimple> simpleList = new ArrayList<>();//样本列表
        Integer scale = dbItem.getScale();
        if (scale!=null && scale!=0){//如果精度不等于零，则只生成一个样本(单位是kg、m这类的物料)
            InspectionBillSimple inspectionBillSimple = new InspectionBillSimple();
            inspectionBillSimple.setBizNo(bill.getBizNo());
            inspectionBillSimple.setSort(1);
            inspectionBillSimple.setSimpleCode(String.format("%04d",1));
            inspectionBillSimple.setDetectQuantity(new BigDecimal(detectList.size()));//检测项数量
            JpaUtil.save(inspectionBillSimple);
            simpleList.add(inspectionBillSimple);//样本
        }else{
            int value = bill.getCheckQuantity().intValue();
            if (value==0){
                value = 1;//处理前端可能输入小数的情况
            }
            for (int i=0;i<value;i++){
                InspectionBillSimple inspectionBillSimple = new InspectionBillSimple();
                inspectionBillSimple.setBizNo(bill.getBizNo());
                inspectionBillSimple.setSort(i+1);
                inspectionBillSimple.setSimpleCode(String.format("%04d",i+1));
                inspectionBillSimple.setDetectQuantity(new BigDecimal(detectList.size()));//检测项数量
                JpaUtil.save(inspectionBillSimple);
                simpleList.add(inspectionBillSimple);//样本
            }
        }
        int detailNum = 0;
        List<InspectionBillCheckDetail> details = new ArrayList<>();
        for (InspectionBillSimple simple:simpleList){
            for (InspectionBillDetect detect:detectList){
                detailNum++;
                InspectionBillCheckDetail detail = new InspectionBillCheckDetail();
                detail.setBizNo(bill.getBizNo());
                detail.setSimpleCode(simple.getSimpleCode());
                detail.setDetectCode(detect.getDetectCode());
                detail.setSort(detailNum);

                //InspectionItem inspectionItem = detect.getInspectionItem();
                detail.setInspectionItemCode(detect.getInspectionItemCode());
                detail.setInspectionItemName(detect.getInspectionItemName());
                detail.setInspectionItemClass(detect.getInspectionItemClass());
                detail.setInspectionItemType(detect.getInspectionItemType());
                detail.setInspectionItemDefectGrade(detect.getInspectionItemDefectGrade());
                detail.setInspectionItemStandard(detect.getInspectionItemStandard());
                detail.setInspectionItemDetectionTool(detect.getInspectionItemDetectionTool());
                detail.setInspectionItemDetectionMethod(detect.getInspectionItemDetectionMethod());
                detail.setInspectionItemTechnicalRequirements(detect.getInspectionItemTechnicalRequirements());

                //InspectionSchemeLine inspectionSchemeLine = detect.getInspectionSchemeLine();
                detail.setComparator(detect.getComparator());
                detail.setTargetValue(detect.getTargetValue());
                detail.setUnits(detect.getUnits());
                detail.setMaxValue(detect.getMaxValue());
                detail.setMinValue(detect.getMinValue());
                if (StringUtils.isNotBlank(detect.getDefaultMeasurement())){
                    detail.setMeasurement(detect.getDefaultMeasurement());//赋默认值
                }
                details.add(detail);
            }
        }
        JpaUtil.save(details);//保存明细
    }
}
